import axios, { AxiosRequestConfig, AxiosResponse } from 'axios';
import type { NetworkRequestOptions } from '../types/index.js';

export class NetworkManager {
  private defaultOptions: NetworkRequestOptions;

  constructor(defaultOptions: NetworkRequestOptions = {}) {
    this.defaultOptions = {
      timeout: 30000,
      retries: 3,
      retryDelay: 1000,
      ...defaultOptions,
    };
  }

  async get<T = any>(url: string, options: NetworkRequestOptions = {}): Promise<T> {
    return this.request<T>('GET', url, undefined, options);
  }

  async post<T = any>(url: string, data?: any, options: NetworkRequestOptions = {}): Promise<T> {
    return this.request<T>('POST', url, data, options);
  }

  async put<T = any>(url: string, data?: any, options: NetworkRequestOptions = {}): Promise<T> {
    return this.request<T>('PUT', url, data, options);
  }

  async delete<T = any>(url: string, options: NetworkRequestOptions = {}): Promise<T> {
    return this.request<T>('DELETE', url, undefined, options);
  }

  private async request<T = any>(
    method: string,
    url: string,
    data?: any,
    options: NetworkRequestOptions = {}
  ): Promise<T> {
    const mergedOptions = { ...this.defaultOptions, ...options };
    const { retries = 3, retryDelay = 1000, ...axiosOptions } = mergedOptions;

    const config: AxiosRequestConfig = {
      method,
      url,
      data,
      timeout: axiosOptions.timeout,
      headers: axiosOptions.headers,
    };

    let lastError: Error;

    for (let attempt = 0; attempt <= retries; attempt++) {
      try {
        const response: AxiosResponse<T> = await axios(config);
        return response.data;
      } catch (error) {
        lastError = error as Error;
        
        if (attempt < retries) {
          await this.delay(retryDelay * Math.pow(2, attempt)); // Exponential backoff
        }
      }
    }

    throw lastError!;
  }

  private delay(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  async downloadFile(url: string, options: NetworkRequestOptions = {}): Promise<Buffer> {
    const mergedOptions = { ...this.defaultOptions, ...options };
    
    const config: AxiosRequestConfig = {
      method: 'GET',
      url,
      responseType: 'arraybuffer',
      timeout: mergedOptions.timeout,
      headers: mergedOptions.headers,
    };

    const response = await axios(config);
    return Buffer.from(response.data);
  }

  async checkUrl(url: string): Promise<boolean> {
    try {
      const response = await axios.head(url, { timeout: 5000 });
      return response.status >= 200 && response.status < 400;
    } catch {
      return false;
    }
  }
}

export const createNetworkManager = (options?: NetworkRequestOptions): NetworkManager => {
  return new NetworkManager(options);
};
